package com.devshephard.jucks.CASDemo;


import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 使用AtomicReference原子引用类解决ABA问题(带版本号的乐观锁解决ABA问题)
 * ABA: ABA问题是由于在短时间内多个线程操作同一个值,由于某个线程拿到了其他线程修改过的值而并未发现其有修改痕迹从而执行更新.
 */
public class CAS_ABAproblem_Demo {

    /**
     * 这里如果泛型是一个包装类,务必要注意对象引用的问题
     * 对于Integer var = ? 在-128 至 127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,
     * 这个区间内的Integer可以直接使用==进行比较.
     * 但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象(即内存空间不同)
     * 由于compareAndSet操作底层是使用 == 进行判断比较,会导致较大的Integer的CAS乐观锁操作失效.
     */
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);

    public static void main(String[] args) {

        new Thread(()->{

            int stamp = atomicStampedReference.getStamp(); //获取当前版本号
            System.out.println("a1 当前版本号：=>" + stamp);

            try{
                TimeUnit.SECONDS.sleep(1); //睡眠1秒钟
            }catch (Exception e){
                e.printStackTrace();
            }

            atomicStampedReference.compareAndSet(1,2,
                    atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1); //CAS操作,并将版本号加1

            System.out.println("a2 当前版本号：=>" + atomicStampedReference.getStamp()); //获取CAS操作后的版本号

            atomicStampedReference.compareAndSet(2,1,
                    atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1); //CAS操作,并将版本号加1

            System.out.println("a3 当前版本号：=>" + atomicStampedReference.getStamp()); //获取第二次CAS操作后的版本号
        },"A").start();


        //乐观锁
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println("b1 当前版本号：=>" + stamp); //获取当前版本号

            try{
                TimeUnit.SECONDS.sleep(2);
            }catch (Exception e){
                e.printStackTrace();
            }

            System.out.println("b2=> 当前最新版本号："+atomicStampedReference.getStamp() + " 当前b2版本号："+ stamp +" 是否修改成功" + atomicStampedReference.compareAndSet(1,6,
                    stamp,stamp + 1));
            System.out.println("b2=> 当前最新版本号：" + atomicStampedReference.getStamp());
        },"B").start();

    }

}
